<!-- 验证码输入框 -->
<template>
  <view class="aster-code-input">
    <view
      class="aster-code-input__item"
      v-for="(item, index) in codeLength"
      :style="itemStyle(index)"
      :key="index"
    >
      <view class="aster-code-input__item__dot" v-if="dot && codeArray.length > index" />
      <text
        v-else
        :style="{
          fontSize: addUnit(fontSize),
          fontWeight: bold ? 'bold' : 'normal',
          color: color,
        }"
        >{{ codeArray[index] }}</text
      >
      <view class="aster-code-input__item__line" v-if="mode === 'line'" :style="lineStyle" />
      <!-- #ifndef APP-PLUS -->
      <view
        v-if="isFocus && codeArray.length === index"
        :style="{ backgroundColor: color }"
        class="aster-code-input__item__cursor"
      />
      <!-- #endif -->
    </view>
    <input
      :disabled="false"
      type="number"
      :focus="focus"
      :value="inputValue"
      :maxlength="maxlength"
      :adjustPosition="true"
      class="aster-code-input__input"
      @input="inputHandler"
      :style="{
        height: addUnit(size),
      }"
      @focus="isFocus = true"
      @blur="isFocus = false"
    />
  </view>
</template>
<script setup lang="ts">
  import { addUnit, getPx } from '@/utils';
  import { computed, nextTick, onMounted, ref } from 'vue';

  const props = defineProps({
    // 最大输入长度
    maxlength: {
      type: [String, Number],
      default: 4,
    },
    // 是否用圆点填充
    dot: {
      type: Boolean,
      default: false,
    },
    // 显示模式，box-盒子模式，line-底部横线模式
    mode: {
      type: String,
      default: 'box',
    },
    // 字符间的距离
    space: {
      type: [String, Number],
      default: 20,
    },
    // 是否自动获取焦点
    focus: {
      type: Boolean,
      default: false,
    },
    // 字体是否加粗
    bold: {
      type: Boolean,
      default: false,
    },
    // 字体颜色
    color: {
      type: String,
      default: '#606266',
    },
    // 字体大小
    fontSize: {
      type: [String, Number],
      default: 36,
    },
    // 输入框的大小，宽等于高
    size: {
      type: [String, Number],
      default: 70,
    },
    // 边框和线条颜色
    borderColor: {
      type: String,
      default: '#c9cacc',
    },
  });

  const emits = defineEmits(['change', 'input', 'finish']);

  const inputValue = ref('');
  const isFocus = ref(false);

  onMounted(() => {
    isFocus.value = props.focus;
  });

  // 根据长度，循环输入框的个数，因为头条小程序数值不能用于v-for
  const codeLength = computed(() => {
    return new Array(Number(props.maxlength));
  });

  // 循环item的样式
  const itemStyle = computed(() => {
    return (index: number) => {
      const style: any = {
        width: addUnit(props.size),
        height: addUnit(props.size),
      };
      // 盒子模式下，需要额外进行处理
      if (props.mode === 'box') {
        // 设置盒子的边框，如果是细边框，则设置为0.5px宽度
        style.border = `1px solid ${props.borderColor}`;
        // 如果盒子间距为0的话
        if (getPx(props.space) === 0) {
          // 给第一和最后一个盒子设置圆角
          if (index === 0) {
            style.borderTopLeftRadius = '3px';
            style.borderBottomLeftRadius = '3px';
          }
          if (index === codeLength.value.length - 1) {
            style.borderTopRightRadius = '3px';
            style.borderBottomRightRadius = '3px';
          }
          // 最后一个盒子的右边框需要保留
          if (index !== codeLength.value.length - 1) {
            style.borderRight = 'none';
          }
        }
      }
      if (index !== codeLength.value.length - 1) {
        // 设置验证码字符之间的距离，通过margin-right设置，最后一个字符，无需右边框
        style.marginRight = addUnit(props.space);
      } else {
        // 最后一个盒子的有边框需要保留
        style.marginRight = 0;
      }
      return style;
    };
  });

  // 将输入的值，转为数组，给item历遍时，根据当前的索引显示数组的元素
  const codeArray = computed(() => {
    return String(inputValue.value).split('');
  });

  // 下划线模式下，横线的样式
  const lineStyle = computed(() => {
    const style: any = {};
    style.height = '4px';
    style.width = addUnit(props.size);
    // 线条模式下，背景色即为边框颜色
    style.backgroundColor = props.borderColor;
    return style;
  });

  // 监听输入框的值发生变化
  const inputHandler = (e: any) => {
    const value = e.detail.value;
    inputValue.value = value;
    // 是否允许输入“.”符号
    nextTick(() => {
      inputValue.value = value.replace('.', '');
    });
    // 未达到maxlength之前，发送change事件，达到后发送finish事件
    emits('change', value);
    // 修改通过v-model双向绑定的值
    emits('input', value);
    // 达到用户指定输入长度时，发出完成事件
    if (String(value).length >= Number(props.maxlength)) {
      emits('finish', value);
    }
  };
</script>
<style lang="scss" scoped>
  view,
  scroll-view,
  swiper-item {
    display: flex;
    flex-direction: column;
    flex-shrink: 0;
    flex-grow: 0;
    flex-basis: auto;
    align-items: stretch;
    align-content: flex-start;
  }

  .aster-code-input {
    display: flex;
    flex-direction: row;
    position: relative;
    overflow: hidden;

    &__item {
      display: flex;
      flex-direction: row;
      justify-content: center;
      align-items: center;
      position: relative;

      &__text {
        font-size: 15px;
        color: #606266;
      }

      &__dot {
        width: 7px;
        height: 7px;
        border-radius: 100px;
        background-color: #606266;
      }

      &__line {
        position: absolute;
        bottom: 0;
        height: 4px;
        border-radius: 100px;
        width: 40px;
        background-color: #606266;
      }
      /* #ifndef APP-PLUS */
      &__cursor {
        position: absolute;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%);
        width: 2rpx;
        height: 40%;
        animation: 1s aster-cursor-flicker infinite;
      }
      /* #endif */
    }

    &__input {
      // 之所以需要input输入框，是因为有它才能唤起键盘
      // 这里将它设置为两倍的屏幕宽度，再将左边的一半移出屏幕，为了不让用户看到输入的内容
      position: absolute;
      left: -750rpx;
      width: 1500rpx;
      top: 0;
      background-color: transparent;
      text-align: left;
    }
  }

  /* #ifndef APP-PLUS */
  @keyframes aster-cursor-flicker {
    0% {
      opacity: 0;
    }
    50% {
      opacity: 1;
    }
    100% {
      opacity: 0;
    }
  }
  /* #endif */
</style>
